The Role of the C# dynamic Keyword

Back in Chapter 3, you learned about the var keyword, which allows you to define local variable in such a way that the underlying date type is determined by the initial assignment (recall that this is termed implicit typing). Once this initial assignment has been made, you have a strongly typed variable, and any attempt to assign an incompatible value will result in a compiler error.

To begin your investigation into the C# dynamic keyword, create a new Console Application named DynamicKeyword. Now, author the following method in your Program class, and verify that the final code statement will indeed trigger a compile time error if uncommented:

static void ImplicitlyTypedVariable()
{
    // a is of type List<int>.
    var a = new List<int>();
    a.Add(90);

    // This would be a compile time error!
    // a = "Hello";
}

Using implicit typing simply for the sake of doing so is considered bad style (if you know you need a List<int>, just declare a List<int>). However, as you have seen in Chapter 13, implicit typing is very useful with LINQ, as many LINQ queries return back enumerations of anonymous class (via projections) which you cannot directly declare in your C# code. However even in such cases, the implicitly typed variable is, in fact, strongly typed.

On a related note, as you also learned back in Chapter 6, System.Object is the top-most parent class in the .NET framework, and can represent anything at all. Again, if you declare a variable of type object, you have a strongly typed piece of data, however what it points to in memory can differ based on your assignment of the reference. In order to gain access to the members the object reference is pointing to in memory, you need to perform an explicit cast.

Assume you have a simple class named Person that defines two automatic properties (FirstName and LastName) both encapsulating a string. Now, observer the following code:

static void UseObjectVarible()
{
    // Assume we have a class named Person.
    object o = new Person() { FirstName = "Mike", LastName = "Larson" };

    // Must cast object as Person to gain access
    // to the Person properties.
    Console.WriteLine("Person's first name is {0}", ((Person)o).FirstName);
}

With the release of .NET 4.0, the C# language now supports a new keyword named dynamic. From a high level, you can consider the dynamic keyword a specialized form of System.Object, in that any value can be assigned to a dynamic data type. At first glance, this can appear horribly confusing, as it appears you now have three ways to define data whose underlying type is not directly indicated in our code base. For example, this method:

static void PrintThreeStrings()
{
    var s1 = "Greetings";
    object s2 = "From";
    dynamic s3 = "Minneapolis";

    Console.WriteLine("s1 is of type: {0}", s1.GetType());
    Console.WriteLine("s2 is of type: {0}", s2.GetType());
    Console.WriteLine("s3 is of type: {0}", s3.GetType());
}

would print out the following if invoked from Main():

s1 is of type: System.String
s2 is of type: System.String
s3 is of type: System.String

What makes a dynamic variable much (much) different from a variable declared implicitly or via a System.Object reference is that it is not strongly typed. Said another way, dynamic data is not statically typed. As far as the C# compiler is concerned, a data point declared with the dynamic keyword can be assigned any initial value at all, and can be reassigned to any new (and possibly unrelated) value during its lifetime. Consider the following method, and the resulting output:

static void ChangeDynamicDataType()
{
    // Declare a single dynamic data point
    // named 't'.
    dynamic t = "Hello!";
    Console.WriteLine("t is of type: {0}", t.GetType());

    t = false;
    Console.WriteLine("t is of type: {0}", t.GetType());

    t = new List<int>();
    Console.WriteLine("t is of type: {0}", t.GetType());
}
t is of type: System.String
t is of type: System.Boolean
t is of type: System.Collections.Generic.List`1[System.Int32]

Now, at this point in your investigation, do be aware that the previous code would compile and execute identically if you were to declare the t variable as a System.Object. However, as you will soon see, the dynamic keyword offers many additional features.

Calling Members on Dynamically Declared Data

Now, given that a dynamic data type can take on the identity of any type on the fly (just like a variable of type System.Object), the next question on your mind might be about calling members on the dynamic variable (properties, methods, indexers, register with events, etc). Well, syntactically speaking, it will again look no different. Just apply the dot operator to the dynamic data variable, specify a public member, and supply any arguments (if required).

However (and this is a very big “however”), the validity of the members you specify will not be checked by the compiler! Remember, unlike a variable defined as a System.Object, dynamic data is not statically typed. It is not until runtime that you will know if you invoked the dynamic data supports a specified member, if you passed in the correct parameters, spelled the member correctly, and so on. Thus, as strange as it might seem, the following method compiles perfectly:

static void InvokeMembersOnDynamicData()
{
    dynamic textData1 = "Hello";
    Console.WriteLine(textData1.ToUpper());

    // You would expect compiler errors here!
    // But they compile just fine.
    Console.WriteLine(textData1.toupper());
    Console.WriteLine(textData1.Foo(10, "ee", DateTime.Now));
}

Notice the second call to WriteLine() attempts to call a method named toupper() on the dynamic data point. As you can see, textData1 is of type string, and therefore you know it does not have a method of this name in all lower case letters. Furthermore, string certainly does not have a method named Foo() which takes an int, string and DataTime object!

Nevertheless, the C# compiler is satisfied. However, if you invoke this method from within Main(), you will get runtime errors similar to the following output:

Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
'string' does not contain a definition for 'toupper'

Another very big distinction between calling members on dynamic data and strongly typed data is that when you apply the dot operator to a piece of dynamic data, you will not see the expected Visual Studio 2010 IntelliSense. Instead, you will find the following general message (see Figure 18-1).

Figure 18-1

Figure 18-1. Dynamic data will not activate IntelliSense

It should make sense that IntelliSense is not possible with dynamic data. However remember that this means you need to be extremely careful when you are typing C# code on such data points! Any misspelling or incorrect capitalization of a member will throw a runtime error, specifically an instance of the RuntimeBinderException class.

The Role of the Microsoft.CSharp.dll Assembly

When you create a new Visual Studio 2010 C# project, you will automatically have a reference set to a new .NET 4.0 assembly named Microsoft.CSharp.dll (you can see this for yourself by looking in the References folder of the Solution Explorer). This library is very small, and only defines a single namespace (Microsoft.CSharp.RuntimeBinder) with two classes (see Figure 18-2).

Figure 18-2

Figure 18-2 The Microsoft.CSharp.dll Assembly

As you can tell by their names, both of these classes are strongly typed exceptions. The most common class, RuntimeBinderException, represents an error which will be thrown if you attempt to invoke a member on a dynamic data type which does not actually exist (as in the case of the toupper() and Foo() methods). This same error will be raised if you specify the wrong parameter data to a member which does exist.

Because dynamic data is so volatile, whenever you are invoking members on a variable declared with the C# dynamic keyword you could wrap the calls within a proper try/catch block, and handle the error in a graceful manner.

static void InvokeMembersOnDynamicData()
{
    dynamic textData1 = "Hello";

    try
    {
        Console.WriteLine(textData1.ToUpper());
        Console.WriteLine(textData1.toupper());
        Console.WriteLine(textData1.Foo(10, "ee", DateTime.Now));
    }
    catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex)
    {
        Console.WriteLine(ex.Message);
    }
}

If you call this method again, you will find the call to ToUpper() (note the capital T and U) works correctly, however you then find the error data displayed to the console:

HELLO
'string' does not contain a definition for'toupper'

Of course, the process of wrapping all dynamic method invocations in a try/catch block is rather tedious. As long as you watch your spelling, and parameter passing, this is not required. However, catching exceptions is handy when you might not know in advance if a member will be present on the target type.

The Scope of the dynamic keyword

Recall that implicitly typed data is only possible for local variables in a member scope. The var keyword can never be used as a return value, a parameter or a member of a class/structure. This is not the case with the dynamic keyword however. Consider the following class definition:

class VeryDynamicClass
{
    // A dynamic field.
    private static dynamic myDynamicField;

    // A dynamic property.
    public dynamic DynamicProperty { get; set; }

    // A dynamic return type and a dynamic paramater type.
    public dynamic DynamicMethod(dynamic dynamicParam)
    {
        // A dynamic local variable.
        dynamic dynamicLocalVar = "Local variable";
    
        int myInt = 10;
        if (dynamicParam is int)
        {
            return dynamicLocalVar;
        }
        else
        {
            return myInt;
        }
    }
}

You could now invoke the public members as expected, however as you are operating on dynamic methods and properties, you cannot be completely sure what the data type will be! To be sure, the VeryDynamicClass definition may not be very useful in a real world application, but it does illustrate the scope of where you can apply this C# keyword.

Limitations of the dynamic Keyword

While a great many things can be defined using the dynamic keyword, there are some limitations regarding its usage. While they are not show stoppers, do know that a dynamic data item cannot make use of lambda expressions or C# anonymous methods when calling a method. For example, the following code will always result in errors, even if the target method does indeed take a delegate parameter which takes a string value and returns void.

dynamic a = GetDynamicObject();

// Error! Methods on dynamic data can’t use lambdas!
a.Method(arg => Console.WriteLine(arg));

To circumvent this restriction, you will need to work with the underlying delegate directly, using the techniques described in Chapter 11 (anonymous methods and lambda expressions, etc). Another limitation is that a dynamic point of data cannot understand any extension methods (see Chapter 12). Unfortunately, this would also include any of the extension methods which come from the LINQ APIs. Therefore, a variable declared with the dynamic keyword has very limited use within LINQ to Objects and other LINQ technologies:

dynamic a = GetDynamicObject();

// Error! Dynamic data can’t find the Select() extension method!
var data = from d in a select d;

Practical Uses of the dynamic Keyword

Given the fact that dynamic data is not strongly typed, not checked at compile time, has no ability to trigger IntelliSense and cannot be the target of a LINQ query, you are absolutely correct to assume that using the dynamic keyword just for the sake of doing so is very poor programming practice.

However, in a few circumstances, the dynamic keyword can radically reduce the amount of code you need to author by hand. Specifically, if you are building a .NET application which makes heavy use of late binding (via reflection), the dynamic keyword can save you typing time. As well, if you are building a .NET application that needs to communicate with legacy COM libraries (such as Microsoft Office products), you can greatly simplify your codebase via the dynamic keyword.

Like any “shortcut,” you need to weigh the pros and cons. The use of the dynamic keyword is a tradeoff between brevity of code, and type safety. While C# is a strongly typed language at its core, you can opt in (or opt out) dynamic behaviors on a call by call basis. Always remember that you never need to use the dynamic keyword. You could always get to the same end result by authoring alternative code by hand (and typically much more of it).

Source Code The DynamicKeyword project is located under the Chapter 18 subdirectory.